package cn.symbio.basic.service.impl;

import cn.symbio.basic.config.BussinessException;
import cn.symbio.basic.constant.BaseConstant;
import cn.symbio.basic.dto.AccountLoginDto;
import cn.symbio.basic.dto.PhoneLoginDto;
import cn.symbio.basic.dto.WechatLoginDto;
import cn.symbio.basic.jwt.JwtUtils;
import cn.symbio.basic.jwt.RsaUtils;
import cn.symbio.basic.jwt.UserInFo;
import cn.symbio.basic.service.ILoginService;
import cn.symbio.basic.util.AjaxResult;
import cn.symbio.basic.util.HttpUtil;
import cn.symbio.basic.util.MD5Utils;
import cn.symbio.basic.util.StrUtils;
import cn.symbio.system.domain.Menu;
import cn.symbio.system.mapper.MenuMapper;
import cn.symbio.system.mapper.PermissionMapper;
import cn.symbio.user.domain.Logininfo;
import cn.symbio.user.domain.User;
import cn.symbio.user.domain.Wxuser;
import cn.symbio.user.mapper.LogininfoMapper;
import cn.symbio.user.mapper.UserMapper;
import cn.symbio.user.mapper.WxuserMapper;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.validation.Valid;
import java.security.PrivateKey;
import java.sql.Struct;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LogininfoMapper logininfoMapper;

    @Autowired
    private WxuserMapper wxuserMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private MenuMapper menuMapper;

    // 使用yml配置注入
    @Value("jwt.rsa.pri")
    private String privateYmlKey;


    /**
     * 账号密码登录
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> accountLogin(AccountLoginDto dto) {
        // 空校验
        // 查询logininfo
        Logininfo logininfo = logininfoMapper.findByUsername(dto.getUsername(),dto.getType());
        // 判断用户是否存在
        if (null == logininfo) {
            throw new BussinessException("账号密码错误！！！！");
        }
        // 数据库的密码是加盐加密的，我们进行比较需加盐加密
        String md5Pwd = MD5Utils.encrypByMd5(logininfo.getSalt() + dto.getPassword());
        // 判断密码是否正确
        if (StringUtils.isBlank(md5Pwd) || !logininfo.getPassword().equals(md5Pwd)) {
            throw new BussinessException("账号或密码错误！！！！");
        }
        return getMap(logininfo);
    }

    @Override
    public Map<String, Object> phoneLogin(PhoneLoginDto dto) {
        // 空校验
        if (StringUtils.isBlank(dto.getPhoneCodeValue())) {
            throw new BussinessException("账号验证码错误1！！！！");
        }
        // 判断手机是否存在
        Logininfo logininfo = logininfoMapper.findByUsername(dto.getPhone(), dto.getType());
        if (null == logininfo) {
            throw new BussinessException("当前用户不存在，请注册！！！！");
        }
        // redis中获取验证码
        String code = (String) redisTemplate.opsForValue().get(String.format(BaseConstant.VerfityCodeCons.REGISTER_SMSCODE_PHONE, dto.getPhone()));
        // 校验短信验证码是否正确
        if (null == code || code.equals(dto.getPhoneCodeValue())){
            throw new BussinessException("账号验证码错误2！！！！");
        }
        return getMap(logininfo);
    }

    /**
     *  数据返回前端
     * @param logininfo
     * @return
     */
    private Map<String, Object> getMap(Logininfo logininfo) {
        // 获取userinfo
        UserInFo userInFo = new UserInFo();
        userInFo.setLogininfo(logininfo);
        // 判断当前用户是后台管理员
        if (0 == logininfo.getType()) {
            // 获取当前登录人权限
            List<String> byPermissions = permissionMapper.findByPermissions(logininfo.getId());
            // 获取菜单权限
            List<Menu> byMenu = menuMapper.findByMenu(logininfo.getId());
            // userinfo赋值
            userInFo.setPermissions(byPermissions);
            userInFo.setMenus(byMenu);
        }
        System.out.println("x:"+privateYmlKey);
        // 获取私钥
        PrivateKey privateKey = RsaUtils.getPrivateKey(this.getClass().getClassLoader().getResource("jwt/pethome_auth_rsa.pri").getFile());

        // 私钥加密token
        String token = JwtUtils.generateTokenExpireInMinutes(userInFo, privateKey, 600);
        // 返回给前端
        Map<String, Object> map = new HashMap<>();
        map.put("token",token);
        map.put("logininfo", logininfo);
        // 这两个也该放在上面判断是后端管理员中，不改了，能跑
        map.put("permissions",userInFo.getPermissions());
        map.put("menus",userInFo.getMenus());
        // 返回
        return map;
    }


//    /**
//     *  数据返回前端
//     * @param logininfo
//     * @return
//     */
//    private HashMap<String, Object> getMap(Logininfo logininfo) {
//        // 生成token
//        String token = StrUtils.getComplexRandomString(32);
//        // 存Redis
//        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
//        logininfo.setPhone("");
//        logininfo.setPassword("");
//        logininfo.setSalt("");
//        // 返回
//        return new HashMap<String,Object>(){{
//            put("token",token);
//            put("logininfo", logininfo);
//        }};
//    }

    /**
     * 微信扫码登录
     * @param dto
     * @return
     */
    @Override
    public AjaxResult wechatLogin(WechatLoginDto dto) {
        // 非空判断
        if (null == dto.getCode()) {
            throw new BussinessException("code不能为空！！！！");
        }
        Pair<String, String> pair = getOpenidAndToken(dto.getCode());
        // 通过openid去wx表查询
        Wxuser wxuser = wxuserMapper.findByOpenId(pair.getRight());
        // 判断wx是否有值并且有userId
        if (null != wxuser && null != wxuser.getUserId()) {
            // 有值直接携带token和logininfo登录
            Logininfo logininfo = logininfoMapper.findByUserIdWx(wxuser.getUserId());
            return AjaxResult.me().setResultObj(getMap(logininfo));
        }
        // 没有值，就手动拼接token和openid返回给前端
        StringBuilder sb = new StringBuilder()
                .append("?accessToken=")
                .append(pair.getLeft())
                .append("&openId=")
                .append(pair.getRight());
        log.info("拼接地址："+sb);
        return AjaxResult.me().setSuccess(false).setResultObj(sb);
    }

    /**
     * 微信绑定
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> wechatBinder(WechatLoginDto dto) {
        // 数据校验
        if (StringUtils.isBlank(dto.getPhone())) {
            throw new BussinessException("手机号不能为空");
        }
        // 第三次访问微信接口，通过token和openid获取用户数据
        String userInfo = HttpUtil.httpGet(String.format(BaseConstant.WechatVerfityCodeCons.WECHAT_LOGIN_USER_INFO, dto.getAccessToken(), dto.getOpenId()));
        // 解析json字符串
        log.info(userInfo);
        // 将JSON字符串转成对应的对象
        Wxuser wxuser = JSONObject.parseObject(userInfo, Wxuser.class);
        // 查询user
        User user = userMapper.findByPhone(dto.getPhone());
        Logininfo logininfo = new Logininfo();
        // 如果等于空就添加三个表即可
        if (null == user) {
            // 拷贝user
            user = userCopyWx(dto);
            // 先保存logininfo
            logininfo = logininfoCopyUser(user);
            logininfoMapper.add(logininfo);
            // 设置logininfoid
            user.setLogininfoId(logininfo.getId());
            // 再保存user表
            userMapper.add(user);
        }
        // 保存wx表
        wxuser.setUserId(user.getId());
        wxuserMapper.add(wxuser);
        return getMap(logininfo);
    }


    private User userCopyWx(WechatLoginDto dto){
        // 盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 加密
        String encryPwd = MD5Utils.encrypByMd5(salt + 1);
        return  User
                .builder()
                .username(dto.getPhone())
                .phone(dto.getPhone())
                .salt(salt)
                .password(encryPwd)
                .state(1)
                .createtime(new Date())
                .build();
    }

    private Logininfo logininfoCopyUser(User user){
        Logininfo logininfo = new Logininfo();
        logininfo.setType(1);
        BeanUtils.copyProperties(user,logininfo);
        return logininfo;
    }

    private Pair<String, String> getOpenidAndToken(String code) {
        // 编写获取token的地址
        String getTokenUrl = String.format(
                BaseConstant.WechatVerfityCodeCons.WECHAT_LOGIN_TOKEN_URL,
                BaseConstant.WechatVerfityCodeCons.WECHAT_LOGIN_APPID,
                BaseConstant.WechatVerfityCodeCons.WECHAT_LOGIN_SECRET,
                code);
        // 使用http工具解析地址获取token，现在他还是一个JSON字符串我们需要转成JSON对象
        String token = HttpUtil.httpGet(getTokenUrl);
        System.out.println(token);
        // 将json字符串转成json对象
        JSONObject jsonObject = JSONObject.parseObject(token);
        // 从json对象中获取token和openId
        String access_token = jsonObject.getString("access_token");
        String openid = jsonObject.getString("openid");
        return Pair.of(access_token,openid);
    }
}
